/*

This file contains all methods related to image postprocessing

*/

__global__ void erodeImageCUDA2(unsigned char *d_imageArray, unsigned char *r_imageArray) //Doesnt work for border pixels - count not incremented.
{
	// get current idx in array
	int idx = (blockIdx.x * blockDim.x) + threadIdx.x;
	int count;

	if (d_imageArray[idx] == 1)
	{		
		count = 0;

		if ((idx >= 640 )							&& (d_imageArray[idx-640] == 1)) count++;			// Pixel above
		if ((idx <= 306559 )						&& (d_imageArray[idx+640] == 1)) count++;			// Pixel below
		if ((idx % 640 != 0 )						&& (d_imageArray[idx-1] == 1))   count++;			// Pixel left
		if ((idx % 640 != 639 )						&& (d_imageArray[idx+1] == 1))   count++;			// Pixel right
		if (((idx >= 640) && (idx % 640 != 639))	&& (d_imageArray[idx-640+1] == 1))count++;		// Top right
		if (((idx <= 306559) && (idx % 640 != 639))	&& (d_imageArray[idx+640+1] == 1))count++;		// Bottom right
		if (((idx >= 640) && (idx % 640 != 0))		&& (d_imageArray[idx-640-1] == 1))count++;		// Top left
		if (((idx <= 306559) && (idx % 640 != 0))	&& (d_imageArray[idx+640-1] == 1))count++;		// Bottom left

		if (count == 8)
			r_imageArray[idx] = 1;
	}
}


__global__ void erodeImageCUDA(unsigned char *d_imageArray, unsigned char *r_imageArray)
{
	// get current idx in array
	int idx = (blockIdx.x * blockDim.x) + threadIdx.x;

	// idx >= width -> ensures that the pixels 0-639 do not get operated on i.e. top row
	// idx <= (height * width) - width -> ensures that bottom row does not get operated on
    // width && idx % width != 0 -> ensures that left most column does not get operated on
	// idx % width != 639 -> ensures that right most column does not get operated on
	//if (idx >= width && idx <= (height * width) - width && idx % width != 0 && idx % width != 639)
	//{
		if (d_imageArray[idx] == 1)
		{		
			//if(		((idx - 640 < 0)									|| ((idx - 640 >=0)								&& (d_imageArray[idx-640] == 1)))			&&		\
			//		((idx + 640 > 307199)								|| ((idx + 640 <=307199)						&& (d_imageArray[idx+640] == 1)))			&&		\
			//		((idx % 640 == 0)									|| ((idx % 640 !=0)								&& (d_imageArray[idx-1] == 1)))				&&		\
			//		((idx % 640 == 639)									|| ((idx % 640 !=639)							&& (d_imageArray[idx+1] == 1)))				&&		\
			//		(( ((idx % 640) == 0)	|| (idx - 640 < 0))			|| (((idx % 640 != 0)	&& (idx - 640 >=0))		&& (d_imageArray[idx - 640 - 1] == 1)) )	&&		\
			//		(( ((idx % 640) == 639) || (idx - 640 < 0))			|| (((idx % 640 != 639) && (idx - 640 >=0))		&& (d_imageArray[idx - 640 + 1] == 1)) )	&&		\
			//		(( ((idx % 640) == 0)	|| (idx + 640 > 307199))	|| (((idx % 640 != 0)	&& (idx + 640 <=307199))&& (d_imageArray[idx + 640 - 1] == 1)) )	&&		\
			//		(( ((idx % 640) == 639) || (idx + 640 > 307199))	|| (((idx % 640 != 639) && (idx + 640 <=307199))&& (d_imageArray[idx + 640 + 1] == 1)) )			
			if(		((idx - 640 < 0)									|| (d_imageArray[idx-640] == 1) )			&&		\
					((idx + 640 > 307199)								|| (d_imageArray[idx+640] == 1) )			&&		\
					((idx % 640 == 0)									|| (d_imageArray[idx-1] == 1) )				&&		\
					((idx % 640 == 639)									|| (d_imageArray[idx+1] == 1) )				&&		\
					(( ((idx % 640) == 0)	|| (idx - 640 < 0))			|| (d_imageArray[idx - 640 - 1] == 1) )		&&		\
					(( ((idx % 640) == 639) || (idx - 640 < 0))			|| (d_imageArray[idx - 640 + 1] == 1) )		&&		\
					(( ((idx % 640) == 0)	|| (idx + 640 > 307199))	|| (d_imageArray[idx + 640 - 1] == 1) )		&&		\
					(( ((idx % 640) == 639) || (idx + 640 > 307199))	|| (d_imageArray[idx + 640 + 1] == 1) )			
				)
				r_imageArray[idx] = 1;
		}
}


__global__ void dilateImageCUDA(unsigned char *d_imageArray, unsigned char *r_imageArray)
{
	// get current idx in array
	int idx = (blockIdx.x * blockDim.x) + threadIdx.x;

	if (d_imageArray[idx] == 1)
	{
		r_imageArray[idx] = 1;


		//if (idx >= 640 ) r_imageArray[idx-640] = 1;				// Pixel above
		//if (idx <= 306559 ) r_imageArray[idx+640] = 1;			// Pixel below
		//if (idx % 640 != 0 ) r_imageArray[idx-1] = 1;			// Pixel left
		//if ((idx + 1) % 640 < 640 ) r_imageArray[idx+1] = 1;	// Pixel right - Wrong!!
		

		if (idx >= 640 )							r_imageArray[idx-640] = 1;			// Pixel above
		if (idx <= 306559 )							r_imageArray[idx+640] = 1;			// Pixel below
		if (idx % 640 != 0 )						r_imageArray[idx-1] = 1;			// Pixel left
		if (idx % 640 != 639 )						r_imageArray[idx+1] = 1;			// Pixel right
		if ((idx >= 640) && (idx % 640 != 639))		r_imageArray[idx-640+1] = 1;		// Top right
		if ((idx <= 306559) && (idx % 640 != 639))	r_imageArray[idx+640+1] = 1;		// Bottom right
		if ((idx >= 640) && (idx % 640 != 0))		r_imageArray[idx-640-1] = 1;		// Top left
		if ((idx <= 306559) && (idx % 640 != 0))	r_imageArray[idx+640-1] = 1;		// Bottom left



		// Possibly faster??
		//if (idx >= 640 ) {
		//	r_imageArray[idx-640] = 1;								// Pixel above
		//	if (idx % 640 != 639) r_imageArray[idx-640+1] = 1;		// Top right
		//	if (idx % 640 != 0)   r_imageArray[idx-640-1] = 1;		// Top left
		//}
		//if (idx <= 306559 ) {
		//	r_imageArray[idx+640] = 1;								// Pixel below
		//	if (idx % 640 != 639) r_imageArray[idx+640+1] = 1;		// Bottom right
		//	if (idx % 640 != 0)   r_imageArray[idx+640-1] = 1;		// Bottom left	
		//}
		//if (idx % 640 != 0 )	r_imageArray[idx-1] = 1;			// Pixel left
		//if (idx % 640 != 639 )	r_imageArray[idx+1] = 1;			// Pixel right

	}
}

__global__ void removeUnwantedNoiseHeightCUDA(unsigned char *d_imageArray, unsigned char *r_tempArray, int maskSize)
{
	int idx = (blockIdx.x * blockDim.x) + threadIdx.x;
	
	int unwantedHeight = maskSize;
	int heightCount;

	r_tempArray[idx] = d_imageArray[idx];

	// greater than two rows from the bottom and less than two rows from the top (if masksize == 3)
	if ((idx >= WIDTH * (unwantedHeight-1)) && (idx <= (HEIGHT * WIDTH) - ((unwantedHeight-1) * WIDTH)))
	{
		heightCount = 0;
		//check pixels
		if (d_imageArray[idx] == 1)
		{
			heightCount++;
			
			//check above two pixels
			for (int k = 1; k < unwantedHeight; k++)
			{
				if (d_imageArray[idx + (k * WIDTH)] == 1)
					heightCount++;
			}
			
			//check below two pixels
			for (int k = 1; k < unwantedHeight; k++)
			{
					if (d_imageArray[idx - (k * WIDTH)] == 1)
						heightCount++;
			}

			if(heightCount < unwantedHeight)
				r_tempArray[idx] = 0;
		}
	}
}

__global__ void removeUnwantedNoiseWidthCUDA(unsigned char *d_imageArray, unsigned char *r_tempArray, int maskSize)
{
	int idx = (blockIdx.x * blockDim.x) + threadIdx.x;

	int unwantedWidth = maskSize;
	int widthCount;

	r_tempArray[idx] = d_imageArray[idx];

    if ((idx % WIDTH >= (unwantedWidth-1)) && (idx % WIDTH <= 639 - (unwantedWidth-1)))
	{
		widthCount = 0;
		//check pixels
		if (d_imageArray[idx] == 1)
		{
			widthCount++;

			//check right pixels
			for (int k = 1; k < unwantedWidth; k++)
			{
				if (d_imageArray[idx + (k * 1)] == 1)
					widthCount++;
			}
			
			//check left pixels
			for (int k = 1; k < unwantedWidth; k++)
			{
				if (d_imageArray[idx - (k * 1)] == 1)
					widthCount++;
			}

			if(widthCount < unwantedWidth)
				r_tempArray[idx] = 0;
		}
	}
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* Sequential Methods																																		  */
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



// Used to dilate an image
void dilateImage(int height, int width, unsigned char *anArray)
{
    unsigned char *tempArray = (unsigned char*)malloc(sizeof(unsigned char) * height * width);
    int i, j;

    for (i = 0; i < width * height; i++)
    {
        tempArray[i] = anArray[i];
    }

	// Perform logical or operation to dilate image
    for (i = 1; i < height - 1; i++)
    {
        for (j = 1; j < width - 1; j++)
        {
			if (anArray[(i * width) + j] == 0)
			{
				if (anArray[((i - 1) * width) + (j - 1)] == 1 || anArray[((i - 1)* width) + j]  == 1 || 
					anArray[((i - 1) * width) + (j + 1)] == 1 || anArray[(i * width) + (j - 1)] == 1 ||
					anArray[(i * width) + (j + 1)] == 1 || anArray[((i + 1) * width) + (j - 1)] == 1 ||
					anArray[((i + 1) * width) + j] == 1 || anArray[((i + 1) * width) + (j + 1)] == 1)
					{
						tempArray[(i * width) + j] = 1;
					}
			}
        }
    }
	
    for (i = 0; i < width * height; i++)
    {
        anArray[i] = tempArray[i];
    }

	free(tempArray);
}

// used to erode an image
void erodeImage(int height, int width, unsigned char *anArray)
{
	unsigned char *tempArray = (unsigned char*)malloc(sizeof(unsigned char) * height * width);
    int i, j;

	for (i = 0; i < width * height; i++)
    {
        tempArray[i] = anArray[i];
    }

	// Perform logical and operation to erode image
    for (i = 1; i < height - 1; i++)
    {
        for (j = 1; j < width - 1; j++)
        {
			if (anArray[(i * width) + j] == 1)
			{
				if (anArray[((i - 1) * width) + (j - 1)] != 1 || anArray[((i - 1)* width) + j]  != 1 || 
					anArray[((i - 1) * width) + (j + 1)] != 1 || anArray[(i * width) + (j - 1)] != 1 ||
					anArray[(i * width) + (j + 1)] != 1 || anArray[((i + 1) * width) + (j - 1)] != 1 ||
					anArray[((i + 1) * width) + j] != 1 || anArray[((i + 1) * width) + (j + 1)] != 1)
					{
						tempArray[(i * width) + j] = 0;
					}
			}
        }
    }

	for (i = 0; i < width * height; i++)
    {
        anArray[i] = tempArray[i];
    }

	free(tempArray);
}

// Remove all object below a certain size in height
void removeUnwantedNoiseHeight(int height, int width, unsigned char *anArray, int maskSize)
{
	unsigned char* tempArray = (unsigned char*)malloc(sizeof(unsigned char) * height * width);
	int unwantedHeight = maskSize;
	int i, j, heightCount;

	for (i = 0; i < width * height; i++)
    {
        tempArray[i] = anArray[i];
    }

	// search until a pixel value of 1 is found
	// find size of object
	// if the size of the object is less than blobSize then remove

	for (i = 2; i < height - 2; i++)
    {
        for (j = 0; j < width; j++)
        {
			heightCount = 0;

			//check pixels
			if (anArray[(i * width) + j] == 1)
			{
				heightCount++;

				/*
					Shaun code added in for loops instead of manually checking pixel values above and below.
				*/
				//check below two pixels
				for (int k = 1; k < unwantedHeight; k++)
				{
					if (anArray[((i + k) * width) + j] == 1)
						heightCount++;
				}
				//check above two pixels

				for (int k = 1; k < unwantedHeight; k++)
				{
					// Dirty dirty hacks. Needed to include this check to stop array out of bounds if the maskSize was too big.
					if(!((i-k) < 0))
						if (anArray[((i - k) * width) + j] == 1)
							heightCount++;
				}

				/* Marks code

					//check below two pixels
					if (anArray[((i+1) * width) + j] == 1)
					{
						heightCount++;

						if (anArray[((i+2) * width) + j] == 1)
						{
							heightCount++;
						}
					}

					//check above 2 pixels
					if (anArray[((i-1) * width) + j] == 1)
					{
						heightCount++;

						if (anArray[((i-2) * width) + j] == 1)
						{
							heightCount++;
						}
					}
				*/
			}
			//do the check for pixel height and delete where necessary
			if(heightCount < unwantedHeight)
			{
				tempArray[(i * width) + j] = 0;
			}
		}
	}

	for (i = 0; i < width * height; i++)
    {
        anArray[i] = tempArray[i];
    }

	free(tempArray);

}

// Remove all object below a certain size in width
void removeUnwantedNoiseWidth(int height, int width, unsigned char *anArray, int maskSize)
{
	unsigned char* tempArray = (unsigned char*)malloc(sizeof(unsigned char) * height * width);
	//int unwantedWidth = 3;
	int unwantedWidth = maskSize;
	int i, j, widthCount;

	for (i = 0; i < width * height; i++)
    {
        tempArray[i] = anArray[i];
    }

	// search until a pixel value of 1 is found
	// find size of object
	// if the size of the object is less than blobSize then remove

	for (i = 0; i < height; i++)
    {
        for (j = 2; j < width - 2; j++)
        {
			widthCount = 0;

			//check pixels
			if (anArray[(i * width) + j] == 1)
			{
				widthCount++;
				
				/*
					Shaun code added in for loops instead of manually checking pixel values left and right.
				*/
				//check below two pixels
				for (int k = 1; k < unwantedWidth; k++)
				{
					if (anArray[(i * width) + (j+k)] == 1)
						widthCount++;
				}
				//check above two pixels
				for (int k = 1; k < unwantedWidth; k++)
				{
					if (anArray[(i * width) + (j-k)] == 1)
						widthCount++;
				}

				/* Marks code
					//check below two pixels
					if (anArray[( i * width) + (j+1)] == 1)
					{
						widthCount++;

						if (anArray[(i * width) + (j+2)] == 1)
						{
							widthCount++;
						}
					}

					//check above 2 pixels
					if (anArray[(i * width) + (j-1)] == 1)
					{
						widthCount++;

						if (anArray[(i * width) + (j-2)] == 1)
						{
							widthCount++;
						}
					}
				*/
			}
			//do the check for pixel height and delete where necessary
			if(widthCount < unwantedWidth)
			{
				tempArray[(i * width) + j] = 0;
			}
		}
	}

	for (i = 0; i < width * height; i++)
    {
        anArray[i] = tempArray[i];
    }

	free(tempArray);

}